package com.odin.backgrounddownload.downloader;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import com.huawei.hms.network.file.api.Progress;
import com.huawei.hms.network.file.api.Response;
import com.huawei.hms.network.file.api.exception.NetworkException;
import com.huawei.hms.network.file.core.FileManagerException;
import com.huawei.hms.network.file.download.api.DownloadManager;
import com.huawei.hms.network.file.download.api.FileRequestCallback;
import com.huawei.hms.network.file.download.api.GetRequest;
import com.odin.backgrounddownload.DownloadParam;
import com.odin.backgrounddownload.Logger;
import com.odin.backgrounddownload.SGFileUtil;

import java.io.Closeable;
import java.io.File;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;

public class SGHmBackGroundDownload extends SGBaseBackGroundDownload
{
    private DownloadManager m_DownloadManager;
    private Queue<DownloadParam> m_WaitQueue = new ArrayDeque<>();
    private ConcurrentHashMap<String, DownloadParam> m_DownLoadingSet = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, DownloadParam> m_FinishSet = new ConcurrentHashMap<>();

    private boolean m_IsPause;
    private long m_DownloadedSize;
    private long m_AllDownloadSize;

    //region 下载回调
    FileRequestCallback m_FileRequestCallback = new FileRequestCallback() {
        @Override
        public GetRequest onStart(GetRequest request) {
            // 文件下载开始时回调此方法。
            Logger.d( "[SGHmBackGroundDownlaod](onStart) " + request.getUrl());
            return request;
        }

        @Override
        public void onProgress(GetRequest request, Progress progress) {
            // 文件下载进度变化时回调此方法。
            String url = GetUrlRelativePath(request.getUrl());
            Logger.d( "[SGHmBackGroundDownlaod](onProgress) " + url);
            DownloadParam downloadParam = m_DownLoadingSet.get(url);
            downloadParam.setFinishSize(progress.getFinishedSize());
        }

        @Override
        public void onSuccess(Response<GetRequest, File, Closeable> response) {
            String url = GetUrlRelativePath(response.getRequest().getUrl());
            Logger.d("[SGHmBackGroundDownlaod](onSuccess) " + url);
            DownloadParam downloadParam = m_DownLoadingSet.get(url);
            FinishDownload(url, 0);
        }

        @Override
        public void onException(GetRequest request, NetworkException exception, Response<GetRequest, File, Closeable> response) {
            // 文件下载过程中网络发生异常，或请求被暂停、取消时回调此方法。
            String url = GetUrlRelativePath(request.getUrl());
            int errorCode = ((FileManagerException)exception.getCause()).getErrorCode();
            Logger.d("[SGHmBackGroundDownlaod](onException) " + url + " errorCode:" + errorCode);
            FinishDownload(url, errorCode);
        }
    };
    //endregion

    public SGHmBackGroundDownload(Activity activity, String cdnUrl, String persistentPath, SGBackGroundDownloadCallback callback)
    {
        super(activity, cdnUrl, persistentPath, callback);

        if (Build.VERSION.SDK_INT >= 23) {
            if (m_UnityMainActivity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
                    m_UnityMainActivity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                m_UnityMainActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1000);
                m_UnityMainActivity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1001);
            }
        }
        // 初始化
        m_DownloadManager = new DownloadManager.Builder("downloadManager").build(m_UnityMainActivity);
    }

    @Override
    public void AddDownload(DownloadParam[] downloadParams)
    {
        // 计算需要下载内容总大小
        for (int i = 0; i < downloadParams.length; i++) {
            m_AllDownloadSize += downloadParams[i].getFileSize();
        }
        // 添加到待下载队列
        m_WaitQueue.addAll(Arrays.asList(downloadParams));
    }

    @Override
    public void ForceDownload(DownloadParam downloadParam)
    {
        StartDownload(downloadParam);
    }

    @Override
    public void StartDownload()
    {
        m_IsPause = false;
        FillDownload();
    }

    @Override
    public void PauseDownload() {
        m_IsPause = true;
    }

    @Override
    public long GetDownloadedSize()
    {
        int downloadingFinishSize = 0;
        for (String key: m_DownLoadingSet.keySet())
        {
            DownloadParam downloadParam = m_DownLoadingSet.get(key);
            downloadingFinishSize += downloadParam.getFinishSize();
        }
        return m_DownloadedSize + downloadingFinishSize;
    }

    @Override
    public long GetAllDownloadSize() {
        return m_AllDownloadSize;
    }

    @Override
    public int QueryDownloadStatus(String urlRelativePath) {
        if (m_FinishSet.containsKey(urlRelativePath))
        {
            return 2;
        }
        if (m_DownLoadingSet.containsKey(urlRelativePath))
        {
            return 1;
        }
        return 0;
    }

    private void FillDownload()
    {
        while (!m_IsPause && !m_WaitQueue.isEmpty() && m_DownLoadingSet.size() < m_MaxDownloadingCount)
        {
            DownloadParam downloadParam = m_WaitQueue.poll();
            StartDownload(downloadParam);

        }
    }

    private void StartDownload(DownloadParam downloadParam)
    {
        if (m_FinishSet.contains(downloadParam.getUrlRelativePath()))
        {
            Logger.d("[SGHmBackGroundDownload](StartDownload) m_FinishSet.contains and return:" + downloadParam.getUrlRelativePath() );
            return;
        }

        // 替换成您需要下载的资源地址。
        String normalUrl = AppendPath(m_CdnUrl, downloadParam.getUrlRelativePath());

        if (m_DownLoadingSet.containsKey(normalUrl))
        {
            // 已经在下载了
            return;
        }
        m_DownLoadingSet.put(normalUrl, downloadParam);

        // 替换成您想要保存的目的地址。
        String downloadFilePath = AppendPath(m_PersistentPath, downloadParam.getTmpSaveRelativePath());
        // 构造GET请求体对象。
        GetRequest getRequest = DownloadManager.newGetRequestBuilder()
                .filePath(downloadFilePath)
                .url(normalUrl)
                .build();

        m_DownloadManager.start(getRequest, m_FileRequestCallback);
    }

    private void FinishDownload(String url, int code)
    {
        DownloadParam param = m_DownLoadingSet.remove(url);
        if (code == 0)
        {
            // 校验下文件大小
            String tmpPath = AppendPath(m_PersistentPath, param.getTmpSaveRelativePath());
            if (GetFileSize(tmpPath) != param.getFileSize())
            {
                // 文件大小校验不通过
                code = 1;
                Logger.w(String.format("[SGHmBackGroundDownload](FinishDownload) download success but size not match.. %s %s", GetFileSize(tmpPath), param.getFileSize()));
            }
            else
            {
                // 校验成功，把 tmp 文件改名
                String targetPath = AppendPath(m_PersistentPath, param.getSaveRelativePath());
                SGFileUtil.Move(tmpPath, targetPath);
            }
        }
        m_FinishSet.put(param.getUrlRelativePath(), param);
        m_DownloadedSize += param.getFileSize();
        UpadteNotice();
        if (m_WaitQueue.isEmpty() && m_DownLoadingSet.isEmpty())
        {
            FinishAllDownload();
            m_SGBackGroundDownloadCallback.OnFinishAll();
        }
        else
        {
            // 触发填充下载
            FillDownload();
            m_SGBackGroundDownloadCallback.OnFinishDownload(url, code);
        }
    }

    private void FinishAllDownload()
    {
        m_SGNotificationHelper.CancelNotification();
        Logger.d1("[SGHmBackGroundDownload](FinishAllDownload)");
    }

    private String GetUrlRelativePath(String url)
    {
        int length = m_CdnUrl.length();
        return url.substring(length);
    }
}
